home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / mops / tiny-rpp.txt < prev    next >
Text File  |  1993-07-23  |  15KB  |  524 lines

  1. ; Mode: Scheme
  2. ;
  3. ;
  4. ; **********************************************************************
  5. ; Copyright (c) 1992 Xerox Corporation.  
  6. ; All Rights Reserved.  
  7. ;
  8. ; Use, reproduction, and preparation of derivative works are permitted.
  9. ; Any copy of this software or of any derivative work must include the
  10. ; above copyright notice of Xerox Corporation, this paragraph and the
  11. ; one after it.  Any distribution of this software or derivative works
  12. ; must comply with all applicable United States export control laws.
  13. ;
  14. ; This software is made available AS IS, and XEROX CORPORATION DISCLAIMS
  15. ; ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
  16. ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  17. ; PURPOSE, AND NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY
  18. ; LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS
  19. ; EXPRESSLY DISCLAIMED, WHETHER ARISING IN CONTRACT, TORT (INCLUDING
  20. ; NEGLIGENCE) OR STRICT LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED
  21. ; OF THE POSSIBILITY OF SUCH DAMAGES.
  22. ; **********************************************************************
  23. ;
  24. ; EDIT HISTORY:
  25. ;
  26. ;      10/**/92  Gregor  Originally Written
  27. ; 1.0  11/10/92  Gregor  Changed names of generic invocation generics.
  28. ;                        Changed compute-getters-and-setters protocol.
  29. ;                        Made comments match the code.
  30. ;                        Changed maximum line width to 72.
  31. ; 1.1  11/24/92  Gregor  Heavily edited to produce the reflective
  32. ; RPP                    processor program that is actually running.
  33. ;                        This is intended to be a tool for discussing
  34. ;                        what the language and protocol should be.
  35. ;                        In the process of doing this, several small
  36. ;                        bugs were discovered, see the tiny-clos.scm
  37. ;                        file.
  38. ; 1.2  12/02/92  Gregor  See tiny-clos.scm.
  39. ; 1.3  12/08/92  Gregor  See tiny-clos.scm.
  40. ;
  41. ;       
  42. (define tiny-clos-version "1.3.RPP")
  43.  
  44. ;
  45. ; A very simple CLOS-like language, embedded in Scheme, with a simple
  46. ; MOP.  The features of the default base language are:
  47. ;
  48. ;   * Classes, with instance slots, but no slot options.
  49. ;   * Multiple-inheritance.
  50. ;   * Generic functions with multi-methods and class specializers only.
  51. ;   * Primary methods and call-next-method; no other method combination.
  52. ;   * Uses Scheme's lexical scoping facilities as the class and generic
  53. ;     function naming mechanism.  Another way of saying this is that
  54. ;     class, generic function and methods are first-class (meta)objects.
  55. ;
  56. ; While the MOP is simple, it is essentially equal in power to both MOPs
  57. ; in AMOP.  This implementation is not at all optimized, but the MOP is
  58. ; designed so that it can be optimized.  In fact, this MOP allows better
  59. ; optimization of slot access extenstions than those in AMOP.
  60. ;
  61. ;
  62. ; In addition to calling a generic, the entry points to the default base
  63. ; language are:
  64. ;
  65. ;   (MAKE-CLASS list-of-superclasses list-of-slot-names)
  66. ;   (MAKE-GENERIC)
  67. ;   (MAKE-METHOD list-of-specializers procedure)
  68. ;   (ADD-METHOD generic method)
  69. ;
  70. ;   (MAKE class . initargs)
  71. ;   (INITIALIZE instance initargs)            ;Add methods to this,
  72. ;                                             ;don't call it directly.
  73. ;   
  74. ;   (SLOT-REF  object slot-name)
  75. ;   (SLOT-SET! object slot-name new-value)
  76. ;
  77. ;
  78. ; So, for example, one might do:
  79. ;
  80. ;   (define <position> (make-class (list <object>) (list 'x 'y)))
  81. ;   (add-method initialize
  82. ;       (make-method (list <position>)
  83. ;         (lambda (call-next-method pos initargs)
  84. ;           (for-each (lambda (initarg-name slot-name)
  85. ;                       (slot-set! pos
  86. ;                                  slot-name
  87. ;                                  (getl initargs initarg-name 0)))
  88. ;                     '(x y)
  89. ;                     '(x y)))))
  90. ;
  91. ;   (set! p1 (make <position> 'x 1 'y 3))
  92. ;
  93. ;
  94. ;
  95. ; NOTE!  Do not use EQUAL? to compare objects!  Use EQ? or some hand
  96. ;        written procedure.  Objects have a pointer to their class,
  97. ;        and classes are circular structures, and ...
  98. ;
  99. ;
  100. ;
  101. ; The introspective part of the MOP looks like the following.  Note that
  102. ; these are ordinary procedures, not generics.
  103. ;
  104. ;   CLASS-DIRECT-SUPERS
  105. ;   CLASS-DIRECT-SLOTS
  106. ;   CLASS-CPL
  107. ;   CLASS-SLOTS
  108. ;
  109. ;   GENERIC-METHODS
  110. ;
  111. ;   METHOD-SPECIALIZERS
  112. ;   METHOD-PROCEDURE
  113. ;
  114. ;
  115. ; The intercessory protocol looks like (generics in uppercase):
  116. ;
  117. ;   make                        
  118. ;     ALLOCATE-INSTANCE
  119. ;     INITIALIZE                   (really a base-level generic)
  120. ;
  121. ;   class initialization
  122. ;     COMPUTE-CPL
  123. ;     COMPUTE-SLOTS
  124. ;     COMPUTE-GETTER-AND-SETTER
  125. ;
  126. ;   add-method                     (Notice this is not a generic!)
  127. ;     COMPUTE-APPLY-GENERIC
  128. ;       COMPUTE-METHODS
  129. ;         COMPUTE-METHOD-MORE-SPECIFIC?
  130. ;       COMPUTE-APPLY-METHODS
  131. ;
  132.  
  133. ;
  134. ; As for the low-level memory system, assume the existence of:
  135. ;
  136. ;  %allocate-instance (nfields)
  137. ;  %instance-ref      (instance field-number)
  138. ;  %instance-set!     (instance field-number new)
  139. ;  
  140. ;  %allocate-entity   (nfields)
  141. ;  %entity-ref        (instance field-number)
  142. ;  %entity-set!       (instance field-number new)
  143. ;
  144. ;  class-of           (any-object)
  145. ;
  146.  
  147.  
  148. (define <top>          (make <class>
  149.                  'direct-supers (list)
  150.                  'direct-slots  (list)))
  151.  
  152. (define <object>       (make <class>
  153.                  'direct-supers (list <top>)
  154.                  'direct-slots  (list)))
  155.  
  156. (define <class>
  157.     (make <class>
  158.       'direct-supers (list <object>)
  159.       'direct-slots  
  160.          (list 'direct-supers           ;(class ...)
  161.            'direct-slots            ;((name . options) ...)
  162.            'cpl                     ;(class ...)
  163.            'slots                   ;((name . options) ...)
  164.            'nfields                 ;an integer
  165.            'field-initializers      ;(proc ...)
  166.            'getters-n-setters)))    ;((slot-name getter setter) ...)
  167.  
  168. (define <primitive-class>
  169.     (make <class>
  170.       'direct-supers (list <class>)
  171.       'direct-slots  (list)))
  172.  
  173. (define make-primitive-class
  174.     (lambda class
  175.       (make (if (null? class) <primitive-class> (car class))
  176.         'direct-supers (list <top>)
  177.         'direct-slots  (list))))
  178.  
  179. (define <boolean>   (make-primitive-class))
  180. (define <symbol>    (make-primitive-class))
  181. (define <char>      (make-primitive-class))
  182. (define <vector>    (make-primitive-class))
  183. (define <pair>      (make-primitive-class))
  184. (define <number>    (make-primitive-class))
  185. (define <string>    (make-primitive-class))
  186. (define <procedure> (make-primitive-class <procedure-class>))
  187.  
  188.  
  189. (define <procedure-class> (make <class>
  190.                 'direct-supers (list <class>)
  191.                 'direct-slots  (list)))
  192.  
  193. (define <entity-class>    (make <class>
  194.                     'direct-supers (list <procedure-class>)
  195.                     'direct-slots  (list)))
  196.  
  197. (define <generic>         (make <entity-class>
  198.                     'direct-supers (list <object>)
  199.                     'direct-slots  (list 'methods)))
  200.  
  201. (define <method>          (make <class>
  202.                     'direct-supers (list <object>)
  203.                     'direct-slots  (list 'specializers
  204.                              'procedure)))
  205.  
  206. ;
  207. ; To make the introspective MOP cleaner, we hide the slot names, in the
  208. ; usual CLOS style.  The following are the acccessors which should be
  209. ; used to access information stored in metaobjects.
  210. ;
  211. ;
  212. (define class-direct-slots
  213.     (lambda (class) (slot-ref class 'direct-slots)))
  214. (define class-direct-supers
  215.     (lambda (class) (slot-ref class 'direct-supers)))
  216. (define class-slots
  217.     (lambda (class) (slot-ref class 'slots)))
  218. (define class-cpl
  219.     (lambda (class) (slot-ref class 'cpl)))
  220.  
  221. (define generic-methods
  222.     (lambda (generic) (slot-ref generic 'methods)))
  223.  
  224. (define method-specializers
  225.     (lambda (method) (slot-ref method 'specializers)))
  226. (define method-procedure
  227.     (lambda (method) (slot-ref method 'procedure)))
  228.  
  229.  
  230. ;
  231. ; The initialization protocol
  232. ;
  233. (define initialize (make-generic))
  234.         
  235.  
  236. ;
  237. ; The instance structure protocol.
  238. ;
  239. (define allocate-instance (make-generic))
  240. (define compute-getter-and-setter (make-generic))
  241.  
  242.  
  243. ;
  244. ; The class initialization protocol.
  245. ;
  246. (define compute-cpl (make-generic))
  247. (define compute-slots (make-generic))
  248.  
  249. ;
  250. ; The generic invocation protocol.
  251. ;
  252. (define compute-apply-generic         (make-generic))
  253. (define compute-methods               (make-generic))
  254. (define compute-method-more-specific? (make-generic))
  255. (define compute-apply-methods         (make-generic))
  256.  
  257.  
  258.  
  259. (add-method initialize
  260.     (make-method (list <object>)
  261.       (lambda (call-next-method object initargs) object)))
  262.  
  263. (add-method initialize
  264.     (make-method (list <class>)
  265.       (lambda (call-next-method class initargs)
  266.     (call-next-method)
  267.     (slot-set! class
  268.            'direct-supers
  269.            (getl initargs 'direct-supers '()))
  270.     (slot-set! class
  271.            'direct-slots
  272.            (map (lambda (s)
  273.               (if (pair? s) s (list s)))
  274.             (getl initargs 'direct-slots  '())))
  275.     (slot-set! class 'cpl   (compute-cpl   class))
  276.     (slot-set! class 'slots (compute-slots class))
  277.     (let* ((nfields 0)
  278.            (field-initializers '())
  279.            (allocator
  280.         (lambda (init)
  281.           (let ((f nfields))
  282.             (set! nfields (+ nfields 1))
  283.             (set! field-initializers
  284.               (cons init field-initializers))
  285.             (list (lambda (o)   (get-field  o f))
  286.               (lambda (o n) (set-field! o f n))))))
  287.            (getters-n-setters
  288.         (map (lambda (slot)
  289.                (cons (car slot)
  290.                  (compute-getter-and-setter class
  291.                             slot
  292.                             allocator)))
  293.              (slot-ref class 'slots))))
  294.       (slot-set! class 'nfields nfields)
  295.       (slot-set! class 'field-initializers field-initializers)
  296.       (slot-set! class 'getters-n-setters getters-n-setters)))))
  297.  
  298. (add-method initialize
  299.     (make-method (list <generic>)
  300.       (lambda (call-next-method generic initargs)
  301.     (call-next-method)
  302.     (slot-set! generic 'methods '())
  303.     (%set-entity-proc! generic
  304.                (lambda args (error "Has no methods."))))))
  305.  
  306. (add-method initialize
  307.     (make-method (list <method>)
  308.       (lambda (call-next-method method initargs)
  309.     (call-next-method)
  310.     (slot-set! method 'specializers (getl initargs 'specializers))
  311.     (slot-set! method 'procedure    (getl initargs 'procedure)))))
  312.  
  313.  
  314.  
  315. (add-method allocate-instance
  316.     (make-method (list <class>)
  317.       (lambda (call-next-method class)
  318.     (let* ((field-initializers (slot-ref class 'field-initializers))
  319.            (new (%allocate-instance
  320.               class
  321.               (length field-initializers))))
  322.       (let loop ((n 0)
  323.              (inits field-initializers))
  324.         (if (pair? inits)
  325.         (begin
  326.          (%instance-set! new n ((car inits)))
  327.          (loop (+ n 1)
  328.                (cdr inits)))
  329.         new))))))
  330.  
  331. (add-method allocate-instance
  332.     (make-method (list <entity-class>)
  333.       (lambda (call-next-method class)
  334.     (let* ((field-initializers (slot-ref class 'field-initializers))
  335.            (new (%allocate-entity
  336.               class
  337.               (length field-initializers))))
  338.       (let loop ((n 0)
  339.              (inits field-initializers))
  340.         (if (pair? inits)
  341.         (begin
  342.          (%entity-set! new n ((car inits)))
  343.          (loop (+ n 1)
  344.                (cdr inits)))
  345.         new))))))
  346.  
  347.  
  348.  
  349. (add-method compute-cpl
  350.     (make-method (list <class>)
  351.       (lambda (call-next-method class)
  352.     (compute-std-cpl class class-direct-supers))))
  353.  
  354.  
  355. (add-method compute-slots
  356.     (make-method (list <class>)
  357.       (lambda (call-next-method class)
  358.     (let collect ((to-process (apply append
  359.                      (map class-direct-slots
  360.                           (class-cpl class))))
  361.               (result '()))
  362.       (if (null? to-process)
  363.           (reverse result)
  364.           (let* ((current (car to-process))
  365.              (name (car current))
  366.              (others '())
  367.              (remaining-to-process
  368.               (collect-if (lambda (o)
  369.                     (if (eq? (car o) name)
  370.                     (begin
  371.                      (set! others (cons o others))
  372.                      #f)
  373.                     #t))
  374.                   (cdr to-process))))
  375.         (collect remaining-to-process
  376.              (cons (append current
  377.                        (apply append (map cdr others)))
  378.                    result))))))))
  379.  
  380.  
  381. (add-method compute-getter-and-setter
  382.     (make-method (list <class>)
  383.       (lambda (call-next-method class slot allocator)
  384.     (allocator (lambda () '())))))
  385.  
  386. (define make
  387.     (lambda (class . initargs)
  388.       (let ((instance (allocate-instance class)))
  389.     (initialize instance initargs)
  390.     instance)))
  391.  
  392. (define slot-ref
  393.     (lambda (object slot-name)
  394.       (let* ((info   (lookup-slot-info (class-of object) slot-name))
  395.          (getter (list-ref info 0)))
  396.     (getter object))))
  397.  
  398. (define slot-set!
  399.     (lambda (object slot-name new-value)
  400.       (let* ((info   (lookup-slot-info (class-of object) slot-name))
  401.          (setter (list-ref info 1)))
  402.     (setter object new-value))))
  403.  
  404. (define lookup-slot-info
  405.     (lambda (class slot-name)
  406.       (let* ((getters-n-setters (slot-ref class 'getters-n-setters))
  407.          (entry (assq slot-name getters-n-setters)))
  408.     (if (null? entry)
  409.         (error "No slot" slot-name "in instances of" class)
  410.         (cdr entry)))))
  411.  
  412.  
  413. (define add-method
  414.     (lambda (generic method)
  415.       (slot-set! generic
  416.          'methods
  417.          (cons method
  418.                (filter-in
  419.             (lambda (m)
  420.               (not (every eq?
  421.                       (method-specializers m)
  422.                       (method-specializers method))))
  423.             (slot-ref generic 'methods))))
  424.       (%set-entity-proc! generic (compute-apply-generic generic))))
  425.  
  426.  
  427. (add-method compute-apply-generic
  428.     (make-method (list <generic>)
  429.       (lambda (call-next-method generic)
  430.     (lambda args
  431.       ((compute-apply-methods generic)
  432.        ((compute-methods generic) args)
  433.        args)))))
  434.  
  435. (add-method compute-methods
  436.     (make-method (list <generic>)
  437.       (lambda (call-next-method generic)
  438.     (lambda (args)
  439.       (let ((applicable
  440.          (filter-in (lambda (method)
  441.                   ;
  442.                   ; Note that every only goes as far as the
  443.                   ; shortest list!
  444.                   ;
  445.                   (every applicable?
  446.                      (method-specializers method)
  447.                      args))
  448.                 (generic-methods generic))))
  449.         (gsort (lambda (m1 m2)
  450.              ((compute-method-more-specific? generic)
  451.               m1
  452.               m2
  453.               args))
  454.            applicable))))))
  455.  
  456. (add-method compute-method-more-specific?
  457.     (make-method (list <generic>)
  458.       (lambda (call-next-method generic)
  459.     (lambda (m1 m2 args)
  460.       (let loop ((specls1 (method-specializers m1))
  461.              (specls2 (method-specializers m2))
  462.              (args args))
  463.         (cond ((null? specls1) (return #t))     ;*Maybe these two
  464.           ((null? specls2) (return #f))     ;*should barf?
  465.           ((null? args)
  466.            (error "Fewer arguments than specializers."))
  467.           (else
  468.            (let ((c1  (car specls1))
  469.              (c2  (car specls2))
  470.              (arg (car args)))
  471.              (if (eq? c1 c2)
  472.              (loop (cdr specls1)
  473.                    (cdr specls2)
  474.                    (cdr args))
  475.              (more-specific? c1 c2 arg))))))))))
  476.  
  477. (define applicable?
  478.     (lambda (c arg)
  479.       (memq c (class-cpl (class-of arg)))))
  480.  
  481. (define more-specific?
  482.     (lambda (c1 c2 arg)
  483.       (memq c2 (memq c1 (class-cpl (class-of arg))))))
  484.  
  485. (add-method compute-apply-methods
  486.     (make-method (list <generic>)
  487.       (lambda (call-next-method generic)
  488.     (lambda (methods args)
  489.       (letrec ((one-step
  490.              (lambda (tail)
  491.                (lambda ()
  492.              (if (null? tail)
  493.                  (error "No applicable methods/next methods.")
  494.                  (apply (method-procedure (car tail))
  495.                     (cons (one-step (cdr tail)) args)))))))
  496.         ((one-step methods)))))))
  497.  
  498.  
  499.  
  500.  
  501. ;
  502. ; So that the normal base-level user can live life without knowing there
  503. ; is a MOP, we supply the following convenient syntax.
  504. ;
  505. ;
  506. (define make-class
  507.     (lambda (direct-supers direct-slots)
  508.       (make <class>
  509.         'direct-supers direct-supers
  510.         'direct-slots  direct-slots)))
  511.  
  512. (define make-generic
  513.     (lambda ()
  514.       (make <generic>)))
  515.  
  516. (define make-method
  517.     (lambda (specializers procedure)
  518.       (make <method>
  519.         'specializers specializers
  520.         'procedure    procedure)))
  521.  
  522.  
  523.